iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0
Modern Web

起步Go!Let's Go!系列 第 14

[ Day 14 ] Go 陣列:創建、賦能、巡禮

  • 分享至 

  • xImage
  •  

陣列

按照順序,存放多個相同型態資料的容器。
與上一張結構的容器概念不一樣的是,結構沒有順序性,且存放的可以是各種資料型態。
大多數的程式語言中,有一個稱為陣列的東西,它可以用一個變數名稱代替多個變數值。
當然 Go 也有,但是它有幾個限制:

  1. 同一個陣列中,只能放相同型態的值,例如:整數陣列、浮點數陣列、字串陣列。
  2. 陣列的長度宣告過就不能更改,例如:宣告長度為 5 的整數陣列,改陣列的長度就是 5。
  3. 宣告陣列的長度時只能是使用確定不變的數。

陣列資料型態

基本長這樣:
:::info
[長度]資料型態
:::

陣列資料的編號

var arr [4]int
// 陣列[編號]代表陣列中的一個資料欄位
fmt.Println(arr[2])

先宣告變數 arr 的資料型態是長度為 4 的整數陣列。
印出陣列中 index 為 2 的資料。
![](https://i.imgur.com/GWtUuD0.png =300x)
跟 Ruby 一樣,index 從 0 開始算起。

陣列操作

接下來會針對建立陣列、給定資料、巡迴陣列。

宣告陣列變數

在 Go 中是這樣宣告陣列的:

var arr1 [4]int
var arr2 [2]string
var arr3 [3]bool

宣告 arr1 為長度為 4 的整數陣列。
宣告 arr2 為長度為 2 的字串陣列。
宣告 arr3 為長度為 3 的布林陣列。

給定初始陣列資料

可以在宣告陣列變數的同時給定一個初始的陣列資料。
如果沒有給初始陣列資料,Go 會自動給定資料型態的初始值,例如:int 就會是 [0 0 0 0]、string 就會是 [ ] ...。
在 Go 是這樣宣告陣列變數及給定陣列資料:

var arr1 [4]int = [4]int{3, 10, -2, 0}
var arr1 [2]string = [2]string{"Hello", "World"}
var arr1 [3]bool = [3]bool{true, false, true}

:::warning
如果宣告時是給int 那資料就必須是 int;是 string 就必須是 string。
而且數量也必須要相同,數量是 4 就必須是 4。
:::

我們也可以省略宣告變數型態,因為編譯器可以從初始化的值中推斷出型別。如果使用這種方式,則必須要給出陣列的長度。

package main
import "fmt"
func main(){
    var fruit = [6]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    fmt.Println(fruit[2])
}

執行結果:
lemon

我們也可以用換行的寫法:

package main
import "fmt"
func main(){
    fruit := [6]string{
        "apple",
        "banana",
        "lemon",
        "pineapple",
        "watermelon",
        "onange",
    }
    fmt.Println(fruit[4])
}

執行結果:
watermelon

但是這邊要記得在結尾加上 ,,如果不加會發生編譯錯誤的訊息。
如果你懶的話,也可以這樣寫:

package main
import "fmt"
func main(){
    fruit := [6]string{
        "apple",
        "banana",
        "lemon",
        "pineapple",
        "watermelon",
        "onange"}
    fmt.Println(fruit[4])
}

執行結果:
watermelon

這樣就不用在最後還要加,

題外話:
Go Array 跟 Ruby Array來做比較:

arr1 = [3, 10, -2, 0]
arr2 = ["Hello", "World"]
arr3 = [true, false, true]
# 在 Ruby 中甚至不用在意資料型態
arr4 = [3, "Hi", true]

逐一給定陣列資料

也可以不用在一開始的時候就給定陣列資料。
在一開始先宣告一個預設的初始值,接著在一個一個給定。如下:

var arr [4]int
arr[0] = 3
arr[1] = 10
arr[2] = -2
arr[3] = 0
package main
import "fmt"
func main(){
    var fruit [6]string
    fruit[0] = "apple"
    fruit[1] = "banana"
    fruit[2] = "lemon"
    fruit[3] = "pineapple"
    fruit[4] = "watermelon"
    fruit[5] = "onange"
    fmt.Println(fruit[3])
}

執行結果:
pineapple

當然也可以不用全部都給:

package main
import "fmt"
func main(){
    var fruit [6]string
    fruit[0] = "apple"
    fruit[1] = "banana"
    fruit[2] = "lemon"
    fruit[3] = "pineapple"
    fmt.Println(fruit[5])
}

執行結果:
(空無一物)

執行結果會是空無一物,這是上面有提過的,Go 在沒有給定初始資料時,會自動給定資料型態的初始值。
上面的程式碼是字串陣列,所以給的初始值就會是空字串。
這樣有預設值的設計不只出現在陣列,在宣告一般變數沒有給值,Go 會很貼心的給你預設值。

package main
import "fmt"
func main(){
    var n int
    fmt.Println(n)
}

執行結果:
0

宣告陣列簡化

還記得 := 嗎?在宣告變數時很常用,在宣告陣列時,也可以去使用。

package main
import "fmt"
func main(){
    fruit := [6]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    fmt.Println(fruit[2])
}

執行結果:
banana

上面有提到宣告陣列時,要給定陣列長度,但是當遇到很長的陣列時,可以叫 Go 來幫你算。
只要在 [] 加上 ...,這樣 Go 就會幫你算好剛好的長度。

package main
import "fmt"
func main(){
    fruit := [...]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    fmt.Println(fruit[5])
}

執行結果:
orange

取得陣列內的值

要怎麼取得陣列的值?
:::info
陣列名稱[index]
:::
這樣就可以取得陣列內指定位置的值。
但有一點要注意,當要求超過陣列長度的值,就會出現錯誤訊息。

package main
import "fmt"
func main(){
    fruit := [...]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    fmt.Println(fruit[6])
}

執行結果:
invalid argument: index 6 out of bounds [0:6] (exit status 1)

取得陣列長度

要怎麼取得陣列長度,在 Go 中,用 len 來取得:

var arr1 [4]int = [4]int{3, 10, -2. 0}
var arr2 [2]string = [2]string{"Hello","World"}
// 利用 len(陣列) 取得陣列長度
fmt.Println(len(arr1))  // 4
fmt.Println(len(arr2))  // 2

巡迴陣列資料

在 Go 中,會用 for loop 搭配 Array,去得到 Array 中每一筆資料。
像是 JS 的 forEach,Ruby 的 each。

var arr1 [4]int = [4]int{3, 10, -2, 0}
var index int
for index=0; index < len(arr); index ++{
    fmt.Println(arr[index])
}
package main
import "fmt"
func main(){
    fruit := [...]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    for i := 0; i < len(fruit); i++{
        fmt.Println(fruit[i])
    }
}

執行結果:
apple
banana
lemon
pineapple
watermelon
onange

除了用 for loop 還可以用 range
Go 提供了 range 來走訪陣列,這個方法可以用來走訪所有有「迭代器」的資料結構,後面會在解釋「迭代器」是甚麼。

package main
import "fmt"
func main(){
    fruit := [...]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    for k, v := range fruit {
        fmt.Printf("fruit[%d]: %s\n", k, v)
    }
}

執行結果:
fruit[0]: apple
fruit[1]: banana
fruit[2]: lemon
fruit[3]: pineapple
fruit[4]: watermelon
fruit[5]: onange

k 代表的是 key: 0,1,2,3,4,5
v 代表的是 value: "apple", "banana", "lemon", "pineapple", "watermelon", "onange"

如果不需要其中一個值,可以用之前學過的 _ 去忽略。

package main
import "fmt"
func main(){
    fruit := [...]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    for _, v := range fruit {
        fmt.Printf("%s\n",v)
    }
}

執行結果:
apple
banana
lemon
pineapple
watermelon
onange

另外使用range 巡迴陣列時,直接更改 value 並不會對陣列內的值造成影響,但仍然可以透過 key 去更改。
更改 value:

package main
import "fmt"
func main(){
    fruit := [...]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    for _, v := range fruit {
        v = v + "!"
        fmt.Println(v)
    }
    for _, v := range fruit {
        fmt.Printf("",v)
    }
}

執行結果:
apple!
banana!
lemon!
pineapple!
watermelon!
onange!
apple
banana
lemon
pineapple
watermelon
onange

透過 key 更改value:

package main
import "fmt"
func main(){
    fruit := [...]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    for k, v := range fruit {
        fruit[k] = v + v
    }
    for _, v := range fruit {
        fmt.Println(v)
    }
}

執行結果:
appleapple
bananabanana
lemonlemon
pineapplepineapple
watermelonwatermelon
onangeonange

宣告陣列長度時不能使用變數來宣告

在 Go 中,陣列長度在編譯完後就不能再更動,因此在執行程式前,陣列長度就必需是確定的,變數值是由 Go 發動時才被確定的,所以不能使用變數來設定陣列的長度。Go 的陣列是「很純的陣列」所以才會這樣規定。

package main
import "fmt"
func main(){
    length := 6
    fruit := [length]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    for _, v := range fruit{
        fmt.Println(v)
    }
}

執行結果:
invalid array length length (exit status 1)

如果還是希望透過類似的手法來完成,那可以宣告一個常數(constant)

package main
import "fmt"
func main(){
    const length int = 5
    fruit := [length]string{"apple", "banana", "lemon", "pineapple", "watermelon", "onange"}
    for _, v := range fruit{
        fmt.Println(v)
    }
}

執行結果:
apple
banana
lemon
pineapple
watermelon
onange

宣告成常數後就不能更改其值,因為常數能在編譯前確定,所以可以當作陣列的長度。

package main
import "fmt"
func main(){
    const length int = 5
    length = 7
    fmt.Println(length)
}

執行結果:
cannot assign to length (constant 5 of type int) (exit status 1)

練習

建立陣列

整數陣列

func main() {
    // 整數陣列
    var numbers [4]int
    numbers[0] = 13
    numbers[1] = 111
    numbers[2] = 23
    numbers[3] = 92
    fmt.Println(numbers)  // 印出 [13 111 23 92]
}

如果多給一個會怎樣?

func main() {
    // 整數陣列
    var numbers [4]int
    numbers[0] = 13
    numbers[1] = 111
    numbers[2] = 23
    numbers[3] = 92
    numbers[4] = 22
    fmt.Println(numbers)
}
$ go build array-basic.go
# command-line-arguments
./array-basic.go:12:10: invalid argument: index 4 out of bounds [0:4]

Go 會不允許這樣建立。

字串陣列

func main(){
    var words [2]string
    fmt.Println(words)   
}

這樣會印出 [ ]。
不要擔心,在 Go 會用空格來做區隔。
所以字串陣列沒給初始值,會長成這樣。
先給定初始資料:

func main(){
    var words [2]string = [2]string{"John", "Tom"}
    fmt.Println(words)  // 印出 [John Tom]
}

取得陣列中每一筆資料

計算學生成績總和:

func main(){
    var grades [3]int = [3]int{60, 90, 73}
    var sum int
    var index int
    for index = 0; index < len(grades); index++ {
        sum = sum + grades[index]
        // sum += grades[index]
    }
    fmt.Println(sum / len(grades))
}

讓使用者自己輸入成績:

func main(){
    var grades [4]int
    var index int
    fmt.Println("請輸入成績")
    // 逐一輸入陣列的資料
    for index = 0; index < len(grades); index++ {
        fmt.Scanln(&grades[index])
    }
    // 逐一取得陣列資料並計算
    var sum int
    for index = 0; index < len(grades); index++ {
        sum += grades[index]
    }
    fmt.Println(sum / len(grades))
}

上一篇
[ Day 13] Go 結構魔法:定義、實體化、編織
下一篇
[ Day 15 ] Go 切片魔法:自由改變的程式碼旋律
系列文
起步Go!Let's Go!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言